#ifndef XG_REFLECT_CPP
#define XG_REFLECT_CPP
///////////////////////////////////////////////////////////////////
#include "../Reflect.h"

class ReflectAttrMap
{
	friend class ReflectHelper;

	struct Item
	{
		sp<regex> filter;
		const char* type;
		const char* name;
		const char* remark;
		const char* extdata;
	};

	class Data
	{
		int ofs = -1;
		int end = -1;
		SpinMutex mtx;
		ReflectKey key;
		vector<ReflectItem> vec;
		mutable Item* arr = NULL;

		Data& operator = (const Data& obj)
		{
			ofs = obj.ofs;
			end = obj.end;
			key = obj.key;
			arr = obj.arr;
			obj.arr = NULL;

			return *this;
		}

	public:
		~Data()
		{
			if (arr) free(arr);
		}
		Data(const Data& obj)
		{
			*this = obj;
		}
		Data(ReflectKey id = 0) : key(id)
		{
			arr = (Item*)calloc(0xFF, sizeof(Item));
		}
		bool operator == (const ReflectKey& id) const
		{
			return id == key;
		}

	public:
		Item& get(int index)
		{
			if (index <= ofs || index < 0xFF) return arr[index];
	
			arr = (Item*)realloc(arr, index * sizeof(Item) + sizeof(Item));
			memset(arr + ofs + 1, 0, (index - ofs) * sizeof(Item));

			return arr[index];
		}
		vector<ReflectItem> getAttrList()
		{
			if (end >= ofs) return vec;

			SpinLocker lk(mtx);
			vector<ReflectItem> res;

			for (int i = 0; i <= ofs; i++)
			{
				const Item& item = get(i);

				if (item.name) res.push_back(ReflectItem(i, item.type, item.name, item.remark, item.filter, item.extdata));
			}

			vec = res;
			end = ofs;

			return res;
		}
		void add(int offset, const char* type, const char* name, const initializer_list<const char*>& list)
		{
			if (ofs >= offset) return;

			if (strcmp(type, "int") && strcmp(type, "bool") && strcmp(type, "long") && strcmp(type, "float") && strcmp(type, "double") && strcmp(type, "string")) type = "object";

			SpinLocker lk(mtx);
			size_t len = list.size();
			Item& item = get(offset);

			ofs = offset;
			item.type = type;
			item.name = name;
			item.remark = len > 0 ? list.begin()[0] : "";
			const char* filter = len > 1 ? list.begin()[1] : "";
			item.extdata = len > 2 ? list.begin()[2] : "required";

			if (filter == NULL) filter = "";
			if (item.remark == NULL) item.remark = "";
			if (item.extdata == NULL) item.extdata = "";

			switch (len)
			{
				case 1:
					if (strcmp(item.remark, "required") == 0 || strcmp(item.remark, "optional") == 0)
					{
						item.extdata = item.remark;
						item.remark = "";
					}
					break;
				case 2:
					if (strcmp(filter, "required") == 0 || strcmp(filter, "optional") == 0)
					{
						item.extdata = filter;
						filter = "";
					}
					break;
			}

			if (*filter) item.filter = newsp<regex>(filter);
		}
	};

	SpinMutex mtx;
	vector<Data> arr[10000];

	vector<ReflectItem> getAttrList(ReflectKey key)
	{
		vector<Data>& vec = arr[(key >> 3) % ARR_LEN(arr)];

		for (Data& item : vec) if (item == key) return item.getAttrList();

		return vector<ReflectItem>();
	}
	void add(Object* self, void* data, const char* type, const char* name, const initializer_list<const char*>& list)
	{
		ReflectKey key = ReflectHelper::GetKey(self);
		vector<Data>& vec = arr[(key >> 3) % ARR_LEN(arr)];

		for (Data& item : vec)
		{
			if (item == key)
			{
				item.add((char*)(data) - (char*)(self), type, name, list);

				return;
			}
		}

		{
			SpinLocker lk(mtx);

			for (Data& item : vec)
			{
				if (item == key)
				{
					item.add((char*)(data) - (char*)(self), type, name, list);

					return;
				}
			}

			vec.push_back(Data(key));

			vec.back().add((char*)(data) - (char*)(self), type, name, list);
		}
	}
	void add(Object* self, Object* data, const char* type, const char* name, const initializer_list<const char*>& list)
	{
		ReflectKey key = ReflectHelper::GetKey(data);
		vector<Data>& vec = arr[(key >> 3) % ARR_LEN(arr)];
		if (vec.size() > 0) add(self, (void*)(data), type, name, list);
	}
};

bool ReflectItem::isNumber() const
{
	return strcmp(type, "int") == 0 || strcmp(type, "long") == 0 || strcmp(type, "float") == 0 || strcmp(type, "double") == 0;
}
string ReflectItem::get(const void* obj) const
{
	char* dest = (char*)(obj) + offset;

	if (type == NULL || strcmp(type, "object") == 0) return stdx::EmptyString();

	if (strcmp(type, "int") == 0) return stdx::str(*(int*)(dest));

	if (strcmp(type, "bool") == 0) return stdx::str(*(bool*)(dest));

	if (strcmp(type, "long") == 0) return stdx::str(*(long*)(dest));

	if (strcmp(type, "float") == 0) return stdx::str(*(float*)(dest));

	if (strcmp(type, "double") == 0) return stdx::str(*(double*)(dest));

	return *(string*)(dest);
}
void ReflectItem::check(const string& val) const
{
	if (val.empty())
	{
		if (strcmp(extdata, "required") == 0) stdx::Throw(XG_PARAMERR, stdx::format("parameter[%s] missing", name));
	}
	else
	{
		if (filter && !regex_match(val, *filter)) stdx::Throw(XG_PARAMERR, stdx::format("parameter[%s] format error", name));
	}
}
bool ReflectItem::set(void* obj, long val) const
{
	char* dest = (char*)(obj) + offset;

	if (type == NULL || strcmp(type, "object") == 0) return false;

	if (strcmp(type, "int") == 0)
	{
		*(int*)(dest) = val;
	}
	else if (strcmp(type, "bool") == 0)
	{
		*(bool*)(dest) = val ? true : false;
	}
	else if (strcmp(type, "long") == 0)
	{
		*(long*)(dest) = val;
	}
	else if (strcmp(type, "float") == 0)
	{
		*(float*)(dest) = val;
	}
	else if (strcmp(type, "double") == 0)
	{
		*(double*)(dest) = val;
	}
	else
	{
		*(string*)(dest) = stdx::str(val);
	}

	return true;
}
bool ReflectItem::set(void* obj, double val) const
{
	char* dest = (char*)(obj) + offset;

	if (type == NULL || strcmp(type, "object") == 0) return false;

	if (strcmp(type, "int") == 0)
	{
		*(int*)(dest) = val;
	}
	else if (strcmp(type, "bool") == 0)
	{
		*(bool*)(dest) = val < -0.000001 || val > 0.000001;
	}
	else if (strcmp(type, "long") == 0)
	{
		*(long*)(dest) = val;
	}
	else if (strcmp(type, "float") == 0)
	{
		*(float*)(dest) = val;
	}
	else if (strcmp(type, "double") == 0)
	{
		*(double*)(dest) = val;
	}
	else
	{
		*(string*)(dest) = stdx::str(val);
	}

	return true;
}
bool ReflectItem::set(void* obj, const char* val) const
{
	char* dest = (char*)(obj) + offset;

	if (val == NULL || type == NULL || strcmp(type, "object") == 0) return false;

	if (strcmp(type, "string") == 0)
	{
		*(string*)(dest) = val;
	}
	else
	{
		if (*val == 0) return true;

		if (strcmp(type, "int") == 0)
		{
			*(int*)(dest) = stdx::atoi(val);
		}
		else if (strcmp(type, "bool") == 0)
		{
			*(bool*)(dest) = strcasecmp(val, "true") == 0;
		}
		else if (strcmp(type, "long") == 0)
		{
			*(long*)(dest) = stdx::atol(val);
		}
		else if (strcmp(type, "float") == 0)
		{
			*(float*)(dest) = stdx::atof(val);
		}
		else
		{
			*(double*)(dest) = stdx::atof(val);
		}
	}

	return true;
}

static ReflectAttrMap& GetAttrMap()
{
	static ReflectAttrMap attrmap;

	return attrmap;
}

string ReflectHelper::GetAttrString(ReflectKey key)
{
	string res;
	string gap = ",";
	static ReflectAttrMap& attrmap = GetAttrMap();
	vector<ReflectItem> vec = attrmap.getAttrList(key);

	if (vec.empty()) return res;

	for (auto& item : vec) res += gap + item.getName();

	return res.substr(gap.length());
}
vector<ReflectItem> ReflectHelper::GetAttrList(ReflectKey key)
{
	static ReflectAttrMap& attrmap = GetAttrMap();

	return attrmap.getAttrList(key);
}
ReflectHelper::ReflectHelper(Object* self, void* data, const char* type, const char* name, const initializer_list<const char*>& list)
{
	static ReflectAttrMap& attrmap = GetAttrMap();

	attrmap.add(self, data, type, name, list);
}
ReflectHelper::ReflectHelper(Object* self, Object* data, const char* type, const char* name, const initializer_list<const char*>& list)
{
	static ReflectAttrMap& attrmap = GetAttrMap();

	attrmap.add(self, data, type, name, list);
}
///////////////////////////////////////////////////////////////////
#endif